import {
  DataSource,
  EntityManager,
  EntityTarget,
  Repository,
  ObjectLiteral,
} from "typeorm";
import type { BetterSqlite3ConnectionOptions } from "typeorm/driver/better-sqlite3/BetterSqlite3ConnectionOptions";

import type { BaseDataSourceOptions } from "typeorm/data-source/BaseDataSourceOptions";
import { dir, getModules, __projectDir } from "../node";
import Path from "path";
import type { Partial } from "../common";
export const DEFAULT_DB_PATH = dir("database/main/db.sqlite3", "file");

export interface CreateDbsOptions {
  /** 数据库文件路径，默认为 dir("database/main/db.sqlite3", "file"); */
  dbPath: string;
  /**
   * Models, 默认为__projectDir/backend.js/models文件夹下的所有文件，每个文件导出的default，且该文件export const isExported = true才会导入
   */
  entities: BaseDataSourceOptions["entities"];

  /** DataSource Options */
  options?: Partial<BetterSqlite3ConnectionOptions>;
}

/**
 * 默认为__projectDir/backend.js/models文件夹下的所有文件，得到每个文件导出的default，且
 * 该文件export const isExported = true才会导入
 * @param dirName 文件夹绝对路径
 * @returns 返回每个文件导出的default，且该文件export const isExported = true才会导入
 */
export function autoImportModels(
  dirName: string = Path.join(__projectDir, "backend.js/models")
): CreateDbsOptions["entities"] {
  const models = getModules(dirName);
  return models.filter((m) => m.module.isExported).map((m) => m.module.default);
}

/**
 * 创建一个typeorm-
 * 需要安装yarn add typeorm better-sqlite3
 * @param options 选项
 * @returns 创建一个sqlite3数据库，initialize, 并返回Repository
 * @example
 * // in the main file, run only once
 * import { dbBuilder } from "./DbUtils";
 * const db = dbBuilder();
 * export default db; // which is a Promise
 * // when you need to use the db
 * import db from "./somewhere";
 * import User from "./models/User";
 * async function main() {
 *     const $User = new (await db)(User);
 *     console.log(await $User.find({}));
 * }
 */
export async function dbBuilder(options: Partial<CreateDbsOptions> = {}) {
  const { Repo, initialization } = dbBuilderSync(options);
  await initialization;
  return Repo;
}

/**
 * 创建一个typeorm-
 * 需要安装yarn add typeorm better-sqlite3
 * @param options 选项
 * @returns 创建一个sqlite3数据库，initialize, 并返回Repository
 * @example
 * // in the main file, run only once
 * import dbBuilderSync from "./DbUtils";
 * const db = dbBuilderSync();
 * export default db.Repo; // Repository of the database
 * // when you need to use the db
 * import Repo from "./main";
 * import User from "./models/User";
 * async function main() {
 *     const $User = new Repo(User);
 *     console.log(await $User.find({}));
 * }
 */
export default function dbBuilderSync({
  dbPath = DEFAULT_DB_PATH,
  entities = autoImportModels(),
  options = {},
}: Partial<CreateDbsOptions> = {}) {
  const connection = new DataSource({
    type: "better-sqlite3",
    database: dbPath,
    synchronize: true,
    entities: entities,
    ...options,
  });
  const initialization = connection.initialize();
  const manager0 = new EntityManager(connection);
  return {
    Repo: class Repo<T extends ObjectLiteral> extends Repository<T> {
      constructor(target: EntityTarget<T>, manager = manager0) {
        super(target, manager);
      }
    },
    connection,
    manager: manager0,
    initialization,
  };
}
